"""
技术分析工具模块

提供各种技术分析指标计算和图表模式识别功能
"""

import pandas as pd
import numpy as np
from typing import List, Dict, Any, Optional, Tuple
import logging
from ..models.market_data import StockPrice, TechnicalIndicators
from ..models.analysis_results import TechnicalAnalysis
import time
logger = logging.getLogger(__name__)


class TechnicalAnalysisTools:
    """技术分析工具类"""

    def __init__(self):
        """初始化技术分析工具"""
        logger.info("技术分析工具初始化完成")


    def comprehensive_technical_analysis(
        self,
        prices: List[StockPrice],
        indicators: TechnicalIndicators
    ) -> TechnicalAnalysis:
        """
        综合技术分析

        Args:
            prices: 价格数据列表
            indicators: 技术指标

        Returns:
            TechnicalAnalysis: 技术分析结果
        """
        if not prices:
            return TechnicalAnalysis(
                signal="neutral",
                confidence=0.0,
                reasoning={"error": "无价格数据"}
            )

        try:
            # 技术指标信号
            signals = []
            reasoning = {}

            # 技术指标分析
            current_price = prices[-1].close

            # MACD信号 - 使用交叉检测方法
            macd_signal = self._analyze_macd_crossover(prices)
            if macd_signal:
                signals.append(macd_signal["signal"])
                reasoning["macd"] = macd_signal["reasoning"]

            # RSI信号
            if indicators.rsi:
                if indicators.rsi > 70:
                    signals.append("bearish")
                    reasoning["rsi"] = f"RSI超买({indicators.rsi:.1f})"
                elif indicators.rsi < 30:
                    signals.append("bullish")
                    reasoning["rsi"] = f"RSI超卖({indicators.rsi:.1f})"
                else:
                    signals.append("neutral")
                    reasoning["rsi"] = f"RSI正常区间({indicators.rsi:.1f})"

            # 布林带信号
            if indicators.bb_upper and indicators.bb_lower:
                if current_price > indicators.bb_upper:
                    signals.append("bearish")
                    reasoning["bollinger"] = "价格突破布林带上轨，可能回调"
                elif current_price < indicators.bb_lower:
                    signals.append("bullish")
                    reasoning["bollinger"] = "价格跌破布林带下轨，可能反弹"
                else:
                    signals.append("neutral")
                    reasoning["bollinger"] = "价格在布林带内正常波动"

            # OBV能量潮信号
            obv_signal = self._analyze_obv(prices)
            if obv_signal:
                signals.append(obv_signal["signal"])
                reasoning["obv"] = obv_signal["reasoning"]

            #  price drop
            price_drop = (prices[-1].close - prices[-5].close) / prices[-5].close
            if price_drop < -0.05 and indicators.rsi < 40:
                signals.append("bullish")
                reasoning["price_drop"] = "价格下跌超过5%，RSI低于40，可能反弹"
            # elif price_drop < -0.03 and indicators.rsi < 45:
            #     signals.append("bullish")
            #     reasoning["price_drop"] = "价格下跌超过3%，RSI低于45，可能反弹"
            
            # 技术指标信号判断
            bullish_count = signals.count("bullish")
            bearish_count = signals.count("bearish")
            neutral_count = signals.count("neutral")
            total_signals = len(signals)
            if total_signals == 0:
                final_signal = "neutral"
                confidence = 0.0
            else:
                if bullish_count > bearish_count:
                    final_signal = "bullish"
                    confidence = bullish_count / total_signals
                elif bearish_count > bullish_count:
                    final_signal = "bearish"
                    confidence = bearish_count / total_signals
                else:
                    final_signal = "neutral"
                    confidence = neutral_count / total_signals
            


            # 支撑阻力分析（使用ATR比率驱动的容忍带与聚类）
            support_resistance = self.identify_support_resistance_v2(prices, indicators.atr_ratio)

            # 策略指标
            strategy_signals = []
            strategy_reasoning = {}

            # 策略分析
            # 趋势分析
            trend_analysis = self.analyze_trend(prices, indicators)
            if trend_analysis:
                strategy_signals.append(trend_analysis["trend"])
                strategy_reasoning["trend"] = trend_analysis["reasoning"]

            # 成交量分析
            volume_analysis = self.analyze_volume(prices, indicators)
            if volume_analysis:
                strategy_signals.append(volume_analysis["volume_signal"])
                strategy_reasoning["volume"] = volume_analysis["reasoning"]
 
             # 动量策略分析
            momentum_analysis = self.analyze_momentum(prices, indicators)
            if momentum_analysis:
                strategy_signals.append(momentum_analysis["momentum_signal"])
                strategy_reasoning["momentum"] = momentum_analysis["reasoning"]

            # 波动率分析
            volatility_analysis = self.analyze_volatility(prices, indicators)
            if volatility_analysis:
                strategy_signals.append(volatility_analysis["volatility_signal"])
                strategy_reasoning["volatility"] = volatility_analysis["reasoning"]

            # 统计套利分析
            stat_arb_analysis = self.analyze_stat_arb(prices, indicators)
            if stat_arb_analysis:
                strategy_signals.append(stat_arb_analysis["stat_arb_signal"])
                strategy_reasoning["stat_arb"] = stat_arb_analysis["reasoning"]

            strategy_weights = {
                "trend": 0.30,
                "volume": 0.25,
                "momentum": 0.25,
                "volatility": 0.15,
                "stat_arb": 0.05
            }
            strategy_final = weighted_signal_combination(
                {
                    "trend": {
                        "signal": trend_analysis["trend"],
                        "confidence": trend_analysis["confidence"]
                    },
                    "volume": {
                        "signal": volume_analysis["volume_signal"],
                    },
                    "momentum": {
                        "signal": momentum_analysis["momentum_signal"],
                        "confidence": momentum_analysis["confidence"]
                    },
                    "volatility": {
                        "signal": volatility_analysis["volatility_signal"],
                    },
                    "stat_arb": {
                        "signal": stat_arb_analysis["stat_arb_signal"],
                        "confidence": stat_arb_analysis["confidence"]
                    }
                },
                strategy_weights
            )

            return TechnicalAnalysis(
                signal=final_signal,
                confidence=confidence,
                reasoning=reasoning,
                support_resistance=support_resistance,
                strategy_signals=strategy_final["signal"],
                strategy_reasoning=strategy_reasoning,
                strategy_confidence=strategy_final["confidence"]
            )

        except Exception as e:
            logger.error(f"技术分析失败: {e}")
            return TechnicalAnalysis(
                signal="neutral",
                confidence=0.0,
                reasoning={"error": f"技术分析失败: {str(e)}"}
            )

    def _safe_float(self, value: Any) -> Optional[float]:
        """安全转换为浮点数"""
        try:
            if pd.isna(value) or value is None:
                return None
            return float(value)
        except (ValueError, TypeError):
            return None

    def _analyze_obv(self, prices: List[StockPrice]) -> Optional[Dict[str, str]]:
        """
        分析OBV能量潮信号

        OBV (On-Balance Volume) 是基于成交量的技术指标，用于衡量市场买卖力量的强度。
        理论依据：
        - 价格上涨时，成交量加入OBV
        - 价格下跌时，成交量从OBV中减去
        - 价格不变时，OBV保持不变
        - OBV趋势通常领先于价格趋势

        Args:
            prices: 价格数据列表

        Returns:
            Optional[Dict[str, str]]: OBV信号和原因，如果无法分析则返回None
        """
        if len(prices) < 20:
            return {"signal": "neutral", "reasoning": "数据不足，无法分析OBV"}

        try:
            # 转换为DataFrame便于计算
            df = pd.DataFrame([{
                'date': p.date,
                'close': p.close,
                'volume': p.volume
            } for p in prices])

            # 计算OBV
            obv = [0]  # 初始OBV值为0
            for i in range(1, len(df)):
                if df['close'].iloc[i] > df['close'].iloc[i - 1]:
                    # 价格上涨，成交量加入OBV
                    obv.append(obv[-1] + df['volume'].iloc[i])
                elif df['close'].iloc[i] < df['close'].iloc[i - 1]:
                    # 价格下跌，成交量从OBV中减去
                    obv.append(obv[-1] - df['volume'].iloc[i])
                else:
                    # 价格不变，OBV保持不变
                    obv.append(obv[-1])

            df['obv'] = obv

            # 计算OBV的5日平均斜率（趋势方向）
            obv_series = pd.Series(obv)
            obv_slope = obv_series.diff().iloc[-5:].mean()
            if obv_slope > 0:
                return {"signal": "bullish", "reasoning": f"OBV上升趋势强劲，5日平均斜率{obv_slope:.0f}"}
            elif obv_slope < 0:
                return {"signal": "bearish", "reasoning": f"OBV下降趋势明显，5日平均斜率{obv_slope:.0f}"}
            else:
                return {"signal": "neutral", "reasoning": f"OBV趋势不明确，5日平均斜率{obv_slope:.0f}"}


        except Exception as e:
            logger.error(f"OBV分析失败: {e}")
            return {"signal": "neutral", "reasoning": "OBV分析失败"}
        

    def _analyze_macd_crossover(self, prices: List[StockPrice]) -> Optional[Dict[str, str]]:
        """
        分析MACD交叉信号

        Args:
            prices: 价格数据列表

        Returns:
            Optional[Dict[str, str]]: MACD信号和原因，如果无法分析则返回None
        """
        if len(prices) < 60:
            return {"signal": "neutral", "reasoning": "数据不足，无法分析MACD交叉"}

        # 转换为DataFrame
        df = pd.DataFrame([{
            'date': p.date,
            'close': p.close
        } for p in prices])

        df = df.sort_values('date').reset_index(drop=True)

        try:
            # 计算MACD指标
            exp1 = df['close'].ewm(span=12, adjust=False).mean()
            exp2 = df['close'].ewm(span=26, adjust=False).mean()
            df['macd'] = exp1 - exp2
            df['signal_line'] = df['macd'].ewm(span=9, adjust=False).mean()

            # 检查是否有足够的数据
            if len(df) < 2:
                return {"signal": "neutral", "reasoning": "MACD数据不足"}

            # 获取最近两期的MACD和信号线数据
            macd_prev = df['macd'].iloc[-2]
            signal_prev = df['signal_line'].iloc[-2]
            macd_curr = df['macd'].iloc[-1]
            signal_curr = df['signal_line'].iloc[-1]

            # 检测交叉信号
            if macd_prev < signal_prev and macd_curr > signal_curr:
                return {"signal": "bullish", "reasoning": "MACD金叉，动能向上"}
            elif macd_prev > signal_prev and macd_curr < signal_curr:
                return {"signal": "bearish", "reasoning": "MACD死叉，动能向下"}
            else:
                return {"signal": "neutral", "reasoning": "MACD无交叉信号"}

        except Exception as e:
            logger.error(f"MACD交叉分析失败: {e}")
            return {"signal": "neutral", "reasoning": "MACD分析失败"}
    

    
    def analyze_trend(self, prices: List[StockPrice], indicators: TechnicalIndicators) -> Dict[str, Any]:
        """
        趋势分析
        
        Args:
            prices: 价格数据列表
            indicators: 已经计算了的指标
        Returns:
            Dict[str, Any]: 趋势分析结果
        """
        if len(prices) < 60:
            return {"trend": "unknown", "strength": 0, "confidence": 0.0, "reasoning": "数据不足"}
        
        # 计算移动平均线
        closes = [p.close for p in prices[-20:]]
        # ma5 = np.mean(closes[-5:])
        # ma10 = np.mean(closes[-10:])
        # ma20 = np.mean(closes[-20:])
        ema8 = indicators.ema8
        ema21 = indicators.ema21
        ema55 = indicators.ema55
    
        current_price = closes[-1]
        
        # 基于 EMA 排列的趋势方向
        if ema8 > ema21 > ema55:
            trend = "bullish"
        elif ema8 < ema21 < ema55:
            trend = "bearish"
        else:
            trend = "neutral"
        
        # 计算 ADX / +DI / -DI
        adx_vals = self._compute_adx(prices, period=14)
        adx_val = adx_vals.get("adx")
        plus_di = adx_vals.get("+di")
        minus_di = adx_vals.get("-di")
        
        # 构建置信度组件
        def clip01(x: float) -> float:
            return max(0.0, min(1.0, x))
        
        if adx_val is None or np.isnan(adx_val):
            adx_core = 0.0
        else:
            # ADX 强度映射：20→0, 40→1（可调）
            adx_core = clip01((adx_val - 20.0) / 20.0)
        
        # DI 一致性与幅度
        di_consistency = 0.0
        if plus_di is not None and minus_di is not None and not (np.isnan(plus_di) or np.isnan(minus_di)):
            di_gap = clip01(abs(plus_di - minus_di) / 100.0)
            if trend == "bullish" and plus_di > minus_di:
                di_consistency = di_gap
            elif trend == "bearish" and minus_di > plus_di:
                di_consistency = di_gap
            else:
                di_consistency = 0.0
        
        # 价位确认：价格相对 EMA8
        if trend == "bullish":
            price_ok = 1.0 if current_price > ema8 else 0.0
        elif trend == "bearish":
            price_ok = 1.0 if current_price < ema8 else 0.0
        else:
            price_ok = 0.0
        
        # 加权合成置信度（仅当存在方向时有效）
        if trend == "neutral":
            confidence = 0.0
        else:
            # 权重：ADX 0.60, DI 0.25, 价位 0.15
            confidence = clip01(0.60 * adx_core + 0.25 * di_consistency + 0.15 * price_ok)
            # 若 DI 与方向不一致，额外衰减
            if di_consistency == 0.0 and adx_core > 0:
                confidence *= 0.7
        
        # 计算价格相对位置
        recent_high = max(closes)
        recent_low = min(closes)
        price_position = (current_price - recent_low) / (recent_high - recent_low) if recent_high != recent_low else 0.5
        
        # 将 strength 与历史口径对齐（0~10），从置信度派生
        strength = round(confidence * 10.0, 2)
        
        reasoning = (
            f"趋势分析信号为：{trend} | "
            f"EMA趋势={trend} | ADX={adx_val:.2f} | +DI={plus_di:.2f} | -DI={minus_di:.2f} | "
            f"ADX分={adx_core:.2f} | DI一致性={di_consistency:.2f} | 价位确认={price_ok:.2f} | 置信度={confidence:.2f}"
            if adx_val is not None and plus_di is not None and minus_di is not None
            else f"EMA趋势={trend} | 指标数据不足，置信度降为{confidence:.2f}"
        )
        
        return {
            "trend": trend,
            "strength": strength,
            "confidence": confidence,
            "price_position": price_position,
            "ema8": ema8,
            "ema21": ema21,
            "ema55": ema55,
            "adx": adx_val,
            "+di": plus_di,
            "-di": minus_di,
            "reasoning": reasoning,
        }
    


    def identify_support_resistance_v2(self, prices: List[StockPrice], atr_ratio: Optional[float]) -> Dict[str, Any]:
        """
        识别支撑/阻力位（增强版）：
        - 使用分形极值作为候选
        - 基于 ATR 比率与现价计算容忍带，进行价位聚类
        - 对聚类按触碰次数、时间衰减、量能进行打分

        Args:
            prices: 价格数据列表（至少50根K线效果较好）
            atr_ratio: ATR/Close 比率（来自已有指标）。若为None或无效，将使用固定百分比容忍带

        Returns:
            Dict[str, Any]: 包含支撑、阻力与强度
        """
        try:
            if prices is None or len(prices) < 50:
                return {"support": 0.0, "resistance": 0.0, "support_strength": 0.0, "resistance_strength": 0.0}

            window = 50
            recent = prices[-window:]
            highs = [p.high for p in recent]
            lows = [p.low for p in recent]
            closes = [p.close for p in recent]
            volumes = [float(p.volume) if p.volume is not None else 0.0 for p in recent]

            current_price = closes[-1]

            # 容忍带：优先使用 ATR 比率（方案A）。若不可用，退化为0.5%价差。
            tolerance = 0.005 * current_price
            if atr_ratio is not None and isinstance(atr_ratio, (int, float)) and atr_ratio > 0:
                atr_value = atr_ratio * current_price
                tolerance = max(0.5 * atr_value, 0.005 * current_price)

            # 1) 提取分形极值候选（记录价格、索引、成交量）
            resistance_candidates: List[Tuple[float, int, float]] = []
            support_candidates: List[Tuple[float, int, float]] = []

            for i in range(2, len(highs) - 2):
                if highs[i] > highs[i-1] and highs[i] > highs[i-2] and highs[i] > highs[i+1] and highs[i] > highs[i+2]:
                    resistance_candidates.append((float(highs[i]), i, volumes[i]))
                if lows[i] < lows[i-1] and lows[i] < lows[i-2] and lows[i] < lows[i+1] and lows[i] < lows[i+2]:
                    support_candidates.append((float(lows[i]), i, volumes[i]))

            # 若没有候选，直接退化为固定比例上下界
            if not resistance_candidates and not support_candidates:
                return {
                    "support": current_price * 0.95,
                    "resistance": current_price * 1.05,
                    "support_strength": 0.0,
                    "resistance_strength": 0.0,
                }

            # 2) 价位聚类（按价格邻近度）
            def cluster_levels(levels: List[Tuple[float, int, float]], tol: float) -> List[Dict[str, Any]]:
                if not levels:
                    return []
                levels_sorted = sorted(levels, key=lambda x: x[0])
                clusters: List[Dict[str, Any]] = []
                current_cluster_prices: List[float] = [levels_sorted[0][0]]
                current_cluster_items: List[Tuple[float, int, float]] = [levels_sorted[0]]

                for price_val, idx_val, vol_val in levels_sorted[1:]:
                    center = float(np.mean(current_cluster_prices))
                    if abs(price_val - center) <= tol:
                        current_cluster_prices.append(price_val)
                        current_cluster_items.append((price_val, idx_val, vol_val))
                    else:
                        clusters.append({
                            "price": float(np.mean(current_cluster_prices)),
                            "items": current_cluster_items[:],
                        })
                        current_cluster_prices = [price_val]
                        current_cluster_items = [(price_val, idx_val, vol_val)]

                # flush last cluster
                clusters.append({
                    "price": float(np.mean(current_cluster_prices)),
                    "items": current_cluster_items[:],
                })
                return clusters

            res_clusters = cluster_levels(resistance_candidates, tolerance)
            sup_clusters = cluster_levels(support_candidates, tolerance)

            # 3) 聚类打分（触碰次数 / 时间衰减 / 量能），随后按侧内归一
            def score_clusters(clusters: List[Dict[str, Any]]) -> None:
                if not clusters:
                    return
                max_touch = max(len(c["items"]) for c in clusters)
                mean_volume = float(np.mean(volumes)) if volumes and np.mean(volumes) > 0 else 1.0
                # 预先计算各cluster的volume_norm最大值用于归一
                volume_norms: List[float] = []
                for c in clusters:
                    v_norm = np.mean([(itm[2] / mean_volume) if mean_volume > 0 else 0.0 for itm in c["items"]])
                    volume_norms.append(float(v_norm))
                max_vn = max(volume_norms) if volume_norms else 1.0

                for idx, c in enumerate(clusters):
                    touches = len(c["items"]) or 1
                    touch_score = (touches / max_touch) if max_touch > 0 else 0.0

                    # 时间衰减：越近越高，age=window-1-idx，使用指数衰减
                    ages = [(window - 1 - itm[1]) for itm in c["items"]]
                    recency_weights = [np.exp(-age / 10.0) for age in ages]
                    recency_score = float(np.mean(recency_weights)) if recency_weights else 0.0

                    volume_norm = volume_norms[idx] if idx < len(volume_norms) else 0.0
                    volume_score = (volume_norm / max_vn) if max_vn > 0 else 0.0

                    c["score"] = 0.5 * touch_score + 0.3 * recency_score + 0.2 * volume_score

            score_clusters(res_clusters)
            score_clusters(sup_clusters)

            # 4) 选择离现价最近的上/下一档
            def pick_nearest(clusters: List[Dict[str, Any]], side: str) -> Tuple[Optional[Dict[str, Any]], float]:
                if side == "resistance":
                    candidates = [c for c in clusters if c["price"] > current_price]
                else:
                    candidates = [c for c in clusters if c["price"] < current_price]
                if not candidates:
                    return None, 0.0
                candidates_sorted = sorted(candidates, key=lambda c: (abs(c["price"] - current_price), -c.get("score", 0.0)))
                picked = candidates_sorted[0]
                # 强度在该侧内归一
                max_score_side = max(c.get("score", 0.0) for c in candidates)
                strength = (picked.get("score", 0.0) / max_score_side) if max_score_side > 0 else 0.0
                return picked, strength

            picked_res, res_strength = pick_nearest(res_clusters, "resistance")
            picked_sup, sup_strength = pick_nearest(sup_clusters, "support")

            resistance_price = float(picked_res["price"]) if picked_res else (current_price * 1.05)
            support_price = float(picked_sup["price"]) if picked_sup else (current_price * 0.95)

            return {
                "support": support_price,
                "resistance": resistance_price,
                "support_strength": float(sup_strength),
                "resistance_strength": float(res_strength),
            }
        except Exception as e:
            logger.error(f"identify_support_resistance_v2 失败: {e}")
            current_price = prices[-1].close if prices else 0.0
            return {
                "support": current_price * 0.95 if current_price else 0.0,
                "resistance": current_price * 1.05 if current_price else 0.0,
                "support_strength": 0.0,
                "resistance_strength": 0.0,
            }
    
    def analyze_volume(self, prices: List[StockPrice], indicators: TechnicalIndicators) -> Dict[str, Any]:
        """
        成交量分析
        
        Args:
            prices: 价格数据列表
            volume_indicators: 已经计算了的成交量指标，包括volume_ma5, volume_ma20, volume_ratio_5, volume_ratio_20, momentum_1w, momentum_1m, momentum_3m, momentum_6m
            
        Returns:
            Dict[str, Any]: 成交量分析结果
        """
        if len(prices) < 20:
            return {"volume_trend": "unknown", "volume_signal": "neutral", "confidence": 0.5}
        
        volumes = [p.volume for p in prices[-20:]]
        closes = [p.close for p in prices[-20:]]
        
        # 考虑和valume_ma5做加权平均
        volume_ma20 = indicators.volume_ma20
        volume_ratio_20 = indicators.volume_ratio_20
        
        # 价量关系分析
        price_change = (closes[-1] - closes[-2]) / closes[-2]
        volume_change = (volumes[-1] - volumes[-2]) / volumes[-2]
        
        # 成交量信号判断
        if volume_ratio_20 > 1.5 and price_change > 0:
            volume_signal = "bullish"
            reasoning = "成交量分析信号为：bullish，放量上涨，买盘积极"
            conf = 0.8
        elif volume_ratio_20 > 1.5 and price_change < 0:
            volume_signal = "bearish"
            reasoning = "成交量分析信号为：bearish，放量下跌，卖盘沉重"
            conf = 0.8
        elif volume_ratio_20 < 0.7:
            volume_signal = "neutral"
            reasoning = "成交量分析信号为：neutral，成交量萎缩，市场观望"
            conf = 0.3
        else:
            volume_signal = "neutral"
            reasoning = "成交量分析信号为：neutral，成交量正常，无明显信号"
            conf = 0.5
        
        return {
            "volume_trend": "increasing" if volume_change > 0 else "decreasing",
            "volume_signal": volume_signal,
            "volume_ratio": volume_ratio_20,
            "reasoning": reasoning,
            "confidence": float(conf)
        }

    def analyze_momentum(self, prices: List[StockPrice], indicators: TechnicalIndicators) -> Dict[str, Any]:
        """
        动量分析（多周期加权 + 量能确认）

        优先使用已计算的指标（momentum_1m/3m/6m，volume_ratio_20），缺失时基于价格序列回退计算。

        Returns:
            Dict[str, Any]: {"momentum_signal", "confidence", "metrics", "reasoning"}
        """
        try:
            # 优先使用预计算动量
            m1 = indicators.momentum_1m
            m3 = indicators.momentum_3m
            m6 = indicators.momentum_6m

            # 量能确认：优先用 volume_ratio_20，否则用 21日均量比
            volume_momentum = float(indicators.volume_ratio_20)


            # 动量综合得分（偏长期）
            momentum_score = 0.2 * float(m1) + 0.3 * float(m3) + 0.5 * float(m6)

            # 生成信号：需量能确认
            if momentum_score > 0.05 and volume_momentum > 1.0:
                momentum_signal = "bullish"
                confidence = min(abs(momentum_score) * 5.0, 1.0)
            elif momentum_score < -0.05 and volume_momentum > 1.0:
                momentum_signal = "bearish"
                confidence = min(abs(momentum_score) * 5.0, 1.0)
            else:
                momentum_signal = "neutral"
                confidence = 0.5

            reasoning = (
                f"动量分析信号为：{momentum_signal} | 动量综合={momentum_score:.4f} | 量能确认={volume_momentum:.2f} | "
                f"m1={float(m1):.4f}, m3={float(m3):.4f}, m6={float(m6):.4f}"
            )

            return {
                "momentum_signal": momentum_signal,
                "confidence": float(confidence),
                "metrics": {
                    "momentum_1m": float(m1),
                    "momentum_3m": float(m3),
                    "momentum_6m": float(m6),
                    "volume_momentum": float(volume_momentum),
                    "momentum_score": float(momentum_score),
                },
                "reasoning": reasoning,
            }
        except Exception as e:
            logger.error(f"动量分析失败: {e}")
            return {
                "momentum_signal": "neutral",
                "confidence": 0.0,
                "metrics": {},
                "reasoning": "动量分析失败",
            }
    
    def _compute_adx(self, prices: List[StockPrice], period: int = 14) -> Dict[str, Any]:
        """
        计算 ADX（Average Directional Index）及 +DI/-DI 指标（最新值）

        Args:
            prices: 价格数据列表（需包含 high/low/close）
            period: 平滑周期，默认14

        Returns:
            Dict[str, Any]: {"adx": float, "+di": float, "-di": float}
                             若数据不足返回空值 None
        """
        if prices is None or len(prices) < max(2, period + 1):
            return {"adx": None, "+di": None, "-di": None}

        df = pd.DataFrame([
            {"date": p.date, "high": p.high, "low": p.low, "close": p.close}
            for p in prices
        ]).sort_values("date").reset_index(drop=True)

        high_low_range = df["high"] - df["low"]
        high_previous_close_range = (df["high"] - df["close"].shift()).abs()
        low_previous_close_range = (df["low"] - df["close"].shift()).abs()
        true_range = pd.concat(
            [high_low_range, high_previous_close_range, low_previous_close_range], axis=1
        ).max(axis=1)

        upward_move = df["high"].diff()
        downward_move = df["low"].shift() - df["low"]

        positive_directional_movement = np.where(
            (upward_move > downward_move) & (upward_move > 0), upward_move, 0.0
        )
        negative_directional_movement = np.where(
            (downward_move > upward_move) & (downward_move > 0), downward_move, 0.0
        )

        positive_directional_movement = pd.Series(positive_directional_movement, index=df.index)
        negative_directional_movement = pd.Series(negative_directional_movement, index=df.index)

        positive_directional_index = 100.0 * (
            positive_directional_movement.ewm(span=period).mean()
            / true_range.ewm(span=period).mean()
        )
        negative_directional_index = 100.0 * (
            negative_directional_movement.ewm(span=period).mean()
            / true_range.ewm(span=period).mean()
        )

        di_sum = (positive_directional_index + negative_directional_index).replace(0, np.nan)
        dx_series = 100.0 * (positive_directional_index - negative_directional_index).abs() / di_sum
        dx_series = dx_series.fillna(0.0)

        adx_series = dx_series.ewm(span=period).mean()

        return {
            "adx": float(adx_series.iloc[-1]),
            "+di": float(positive_directional_index.iloc[-1]),
            "-di": float(negative_directional_index.iloc[-1]),
        }
    

    def identify_support_resistance_bak(self, prices: List[StockPrice]) -> Dict[str, float]:
        """
        识别支撑阻力位(老版本)
        
        Args:
            prices: 价格数据列表
            
        Returns:
            Dict[str, float]: 支撑阻力位
        """
        if len(prices) < 50:
            return {"support": 0, "resistance": 0}
        
        highs = [p.high for p in prices[-50:]]
        lows = [p.low for p in prices[-50:]]
        closes = [p.close for p in prices[-50:]]
        
        # 寻找局部高点和低点
        resistance_levels = []
        support_levels = []
        
        for i in range(2, len(highs) - 2):
            # 局部高点
            if highs[i] > highs[i-1] and highs[i] > highs[i-2] and highs[i] > highs[i+1] and highs[i] > highs[i+2]:
                resistance_levels.append(highs[i])
            
            # 局部低点
            if lows[i] < lows[i-1] and lows[i] < lows[i-2] and lows[i] < lows[i+1] and lows[i] < lows[i+2]:
                support_levels.append(lows[i])
        
        current_price = closes[-1]
        
        # 找到最近的支撑和阻力位
        resistance = min([r for r in resistance_levels if r > current_price], default=current_price * 1.1)
        support = max([s for s in support_levels if s < current_price], default=current_price * 0.9)
        
        return {
            "support": support,
            "resistance": resistance,
            "support_strength": len([s for s in support_levels if abs(s - support) / support < 0.02]),
            "resistance_strength": len([r for r in resistance_levels if abs(r - resistance) / resistance < 0.02])
        }

    def analyze_volatility(self, prices: List[StockPrice], indicators: TechnicalIndicators) -> Dict[str, Any]:
        """
        波动率分析信号（HV20 触发 + 120日体制评估 + ATR/Close微调）
        优先使用 akshare_client 已计算的字段：historical_volatility, volatility_regime, volatility_z_score, atr_ratio。
        缺失时基于价格序列回退计算。

        Returns:
            Dict[str, Any]: {"volatility_signal", "confidence", "metrics", "reasoning"}
        """
        try:
            if prices is None or len(prices) < 30:
                return {"volatility_signal": "neutral", "confidence": 0.0, "metrics": {}, "reasoning": "数据不足"}

            # 从 indicators 读取预计算字段（若存在）
            hv20 = getattr(indicators, 'historical_volatility', None)
            regime = getattr(indicators, 'volatility_regime', None)
            z_sigma = getattr(indicators, 'volatility_z_score', None)
            atr_ratio = getattr(indicators, 'atr_ratio', None)

            use_precomputed = all(v is not None for v in [hv20, regime, z_sigma, atr_ratio])

            if not use_precomputed:
                # 回退：基于价格序列计算
                df = pd.DataFrame([
                    {"date": p.date, "high": p.high, "low": p.low, "close": p.close}
                    for p in prices
                ]).sort_values("date").reset_index(drop=True)

                # HV20: 对数收益率年化标准差
                log_ret = np.log(df['close'] / df['close'].shift(1))
                hv20_series = log_ret.rolling(window=20, min_periods=15).std() * np.sqrt(252)

                # 120日体制与z-score
                mu120 = hv20_series.rolling(window=120, min_periods=60).mean()
                sigma120 = hv20_series.rolling(window=120, min_periods=60).std()
                min120 = hv20_series.rolling(window=120, min_periods=60).min()
                max120 = hv20_series.rolling(window=120, min_periods=60).max()
                range120 = (max120 - min120)

                hv20_val = hv20_series.iloc[-1] if len(hv20_series) else np.nan
                mu120_val = mu120.iloc[-1] if len(mu120) else np.nan
                sigma120_val = sigma120.iloc[-1] if len(sigma120) else np.nan
                min120_val = min120.iloc[-1] if len(min120) else np.nan
                range120_val = range120.iloc[-1] if len(range120) else np.nan

                if pd.notna(hv20_val) and pd.notna(mu120_val) and pd.notna(sigma120_val) and sigma120_val > 0:
                    z_sigma = float((hv20_val - mu120_val) / sigma120_val)
                else:
                    z_sigma = 0.0

                if pd.notna(hv20_val) and pd.notna(min120_val) and pd.notna(range120_val) and range120_val > 0:
                    regime = float((hv20_val - min120_val) / range120_val)
                    regime = max(0.0, min(1.0, regime))
                else:
                    regime = 0.5

                hv20 = float(hv20_val) if pd.notna(hv20_val) else 0.0

                # ATR/Close 比率
                tr_hl = df['high'] - df['low']
                tr_hc = (df['high'] - df['close'].shift(1)).abs()
                tr_lc = (df['low'] - df['close'].shift(1)).abs()
                tr = pd.concat([tr_hl, tr_hc, tr_lc], axis=1).max(axis=1)
                atr14 = tr.rolling(window=14, min_periods=7).mean()
                atr_ratio_series = atr14 / df['close']
                atr_ratio = float(atr_ratio_series.iloc[-1]) if len(atr_ratio_series) else 0.0
                atr_ratio_ma20 = float(atr_ratio_series.rolling(window=20, min_periods=10).mean().iloc[-1]) if len(atr_ratio_series) else None
            else:
                # 当仅有当前点的ATR比率时，不进行20日均值调节
                atr_ratio_ma20 = None

            # 信号规则：低体制且低位 → 预期扩张(bullish)；高体制且高位 → 预期收缩(bearish)
            if regime is not None and z_sigma is not None and regime <= 0.30 and z_sigma <= -1.0:
                volatility_signal = "bullish"
            elif regime is not None and z_sigma is not None and regime >= 0.70 and z_sigma >= 1.0:
                volatility_signal = "bearish"
            else:
                volatility_signal = "neutral"

            # 置信度：|z| 与 |regime-0.5| 越大，置信度越高；随后用 ATR 进行±10%微调
            base_conf = 0.0
            if z_sigma is not None and regime is not None:
                base_conf = min(abs(float(z_sigma)) / 3.0, 1.0) * (0.5 + 0.5 * abs(float(regime) - 0.5))

            if atr_ratio_ma20 is not None and atr_ratio_ma20 > 0:
                atr_boost = 1.1 if float(atr_ratio) > float(atr_ratio_ma20) else 0.9
            else:
                atr_boost = 1.0

            confidence = float(max(0.0, min(1.0, base_conf * atr_boost)))

            reasoning = (
                f"波动率分析信号为：{volatility_signal} | HV20={float(hv20):.4f}, z(120)={float(z_sigma):.2f}, regime(120)={float(regime):.2f}, ATR/Close={float(atr_ratio):.4f}"
            )

            return {
                "volatility_signal": volatility_signal,
                "confidence": confidence,
                "metrics": {
                    "hv20": float(hv20) if hv20 is not None else 0.0,
                    "volatility_z_score": float(z_sigma) if z_sigma is not None else 0.0,
                    "volatility_regime": float(regime) if regime is not None else 0.5,
                    "atr_ratio": float(atr_ratio) if atr_ratio is not None else 0.0,
                },
                "reasoning": reasoning,
            }
        except Exception as e:
            logger.error(f"波动率分析失败: {e}")
            return {
                "volatility_signal": "neutral",
                "confidence": 0.0,
                "metrics": {},
                "reasoning": "波动率分析失败",
            }

    def analyze_stat_arb(self, prices: List[StockPrice], indicators: TechnicalIndicators) -> Dict[str, Any]:
        """
        统计套利分析

        使用已在 indicators 中计算好的统计特征：
        - hurst_exponent 赫斯特指数
        - skewness 偏度
        - kurtosis 峰度

        返回字典包含：
        - stat_arb_signal: bullish/bearish/neutral
        - confidence: 0~1
        - metrics: 各项数值
        - reasoning: 简要说明
        """
        try:
            h = getattr(indicators, 'hurst_exponent', None)
            sk = getattr(indicators, 'skewness', None)
            ku = getattr(indicators, 'kurtosis', None)

            # 仅使用预计算结果，若缺失则返回中性
            values = [h, sk, ku]
            if any(v is None or (isinstance(v, float) and pd.isna(v)) for v in values):
                return {
                    "stat_arb_signal": "neutral",
                    "confidence": 0.0,
                    "metrics": {},
                    "reasoning": "数据不足，缺少统计特征（hurst/skew/kurtosis）"
                }

            # 信号规则（与另一实现对齐）：
            # - Hurst < 0.4 且 Skew > 1 -> bullish
            # - Hurst < 0.4 且 Skew < -1 -> bearish
            # - 否则 neutral
            if float(h) < 0.4 and float(sk) > 1.0:
                signal = "bullish"
                confidence = max(0.0, min(1.0, float((0.5 - float(h)) * 2.0)))
            elif float(h) < 0.4 and float(sk) < -1.0:
                signal = "bearish"
                confidence = max(0.0, min(1.0, float((0.5 - float(h)) * 2.0)))
            else:
                signal = "neutral"
                confidence = 0.5

            reasoning = (
                f"统计套利分析信号为：{signal} | Hurst={float(h):.3f}, Skew={float(sk):.3f}, Kurt={float(ku):.3f}"
            )

            return {
                "stat_arb_signal": signal,
                "confidence": float(confidence),
                "metrics": {
                    "hurst_exponent": float(h),
                    "skewness": float(sk),
                    "kurtosis": float(ku),
                },
                "reasoning": reasoning,
            }
        except Exception as e:
            logger.error(f"统计套利分析失败: {e}")
            return {
                "stat_arb_signal": "neutral",
                "confidence": 0.0,
                "metrics": {},
                "reasoning": "统计套利分析失败",
            }

def weighted_signal_combination(signals: Dict[str, Dict[str, Any]], weights: Dict[str, float]) -> Dict[str, Any]:
    """
    多策略信号加权组合（模块级函数，暂不接线）

    参数：
      - signals: {strategy: {"signal": str, "confidence": float}}
      - weights: {strategy: float}

    返回：{"signal": str, "confidence": float}
    """
    signal_values = {"bullish": 1, "neutral": 0, "bearish": -1}

    weighted_sum = 0.0
    total_confidence = 0.0

    for strategy, sig in signals.items():
        s = sig.get("signal")
        c = sig.get("confidence", 0.0)
        if s not in signal_values or strategy not in weights:
            continue
        numeric_signal = signal_values[s]
        w = float(weights[strategy])
        weighted_sum += numeric_signal * w * float(c)
        total_confidence += w * float(c)

    final_score = (weighted_sum / total_confidence) if total_confidence > 0 else 0.0

    if final_score > 0.2:
        out_signal = "bullish"
    elif final_score < -0.2:
        out_signal = "bearish"
    else:
        out_signal = "neutral"

    return {"signal": out_signal, "confidence": abs(final_score)}